package com.example.common.exception;

import com.example.common.exception.errcode.ErrCodeBase;
import com.example.common.result.Result;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * 全局异常处理器
 */
@ControllerAdvice
@Slf4j
public class GlobalException {

    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public Result handler(HttpServletRequest request, HttpServletResponse response, Exception e) {
        response.setStatus(500);
        log.error(e.getMessage(), e);
        Result result = new Result();

        //系统预知异常
        if (e instanceof BizException) {
            BizException bizException = (BizException) e;
            int code = bizException.getCode();
            result.setCode(code);
            if (bizException.getCode() == ErrCodeBase.UNDEFINED_ERROR.getCode()) {
                result.setMsg(ErrCodeBase.UNDEFINED_ERROR.getMsg());
                result.setData(bizException.getMsg());
            } else {
                result.setMsg(bizException.getMsg());
            }
            return result;
        }

        //请求体丢失
        if (e instanceof HttpMessageNotReadableException) {
            result.setCode(ErrCodeBase.REQUIRED_BODY_MISSING.getCode());
            result.setMsg(ErrCodeBase.REQUIRED_BODY_MISSING.getMsg());
            result.setData(ErrCheckManual.builder()
                    .webDev(new String[]{"1:查看请求体参数是否正确传入,可能原因:没有传入 request body 或者少传漏传字段"})
                    .javaDev(new String[]{"1:查看请求体参数是否正确传入,可能原因:Controller的方法参数没有添加 @RequestBody"})
                    .build());
            return result;
        }

        //空指针异常
        if (e instanceof NullPointerException) {
            result.setCode(ErrCodeBase.NULL_POINTER_EXCEPTION.getCode());
            result.setMsg(ErrCodeBase.NULL_POINTER_EXCEPTION.getMsg());
            //获取异常发生的位置
            StackTraceElement[] stackTrace = e.getStackTrace();
            if (stackTrace != null && stackTrace.length > 0) {
                StackTraceElement ste = stackTrace[0];
                result.setData(ErrCheckManual.builder()
                        .javaDev(new String[]{
                                new StringBuilder().append("异常可能发生在 ")
                                        .append("  类: ").append(ste.getClassName()).append("     ")
                                        .append("方法: ").append(ste.getMethodName()).append("     ")
                                        .append("行数: ").append(ste.getLineNumber())
                                        .toString(),
                                "异常信息: " + e.getMessage()}
                        )
                        .build());
            }
            return result;
        }

        /**
         * 数据校验异常
         */
        if (e instanceof MethodArgumentNotValidException) {
            BindingResult br = ((MethodArgumentNotValidException) e).getBindingResult();
            String tipMsg = "";
            if (br.hasErrors()) {
                StringBuilder sb = new StringBuilder();
                List<ObjectError> errors = br.getAllErrors();
                //多个数据校验不匹配
                for (int i = 0; i < errors.size(); i++) {
                    sb.append(i + 1).append(":").append(errors.get(i).getDefaultMessage()).append(";  ");
                }
                tipMsg = sb.toString();
                tipMsg = tipMsg.substring(0, tipMsg.length() - 2);
            }
            result.setCode(ErrCodeBase.VALIDATED_EXCEPTION.getCode());
            result.setMsg(ErrCodeBase.VALIDATED_EXCEPTION.getMsg() + ": " + tipMsg);
            result.setData(ErrCheckManual.builder()
                    .webDev(new String[]{"1:检查数据参数传入是否有问题"})
                    .javaDev(new String[]{"1:检查数据参数传入是否有问题"})
                    .build());
            return result;
        }

        // SQL 约束异常
        if(e instanceof DataIntegrityViolationException){
            result.setCode(ErrCodeBase.SQL_CONSTRAINT_EXCEPTION.getCode());
            result.setMsg(ErrCodeBase.SQL_CONSTRAINT_EXCEPTION.getMsg());
            //获取异常发生的位置
            StackTraceElement[] stackTrace = e.getStackTrace();
            if (stackTrace != null && stackTrace.length > 0) {
                result.setData(ErrCheckManual.builder()
                        .javaDev(new String[]{"异常信息: " + e.getMessage()})
                        .build());
            }
            return result;
        }

        /**
         * 未处理的异常
         */
        result.setCode(ErrCodeBase.UNDEFINED_ERROR.getCode());
        result.setMsg(ErrCodeBase.UNDEFINED_ERROR.getMsg());
        result.setData(ErrCheckManual.builder()
                .javaDev(new String[]{"1:添加ErrCode并在异常发生出抛出BizException", "2:在GlobalException根据异常类型处理异常"})
                .build());
        return result;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    public static class ErrCheckManual {
        @Builder.Default
        private String title = "异常排查手册";
        @Builder.Default
        private String[] webDev = {};
        @Builder.Default
        private String[] javaDev = {};
    }

}